
/*
	(c) Galustyan Sergey 2009
*/

#include "dxut.h"
#include "Land.h"

namespace TestTask
{
	const float landxh = (float)(landx >> 1);
	const float landyh = (float)(landy >> 1);

	#define foreach_landxy(bound) for(UINT x = 0; x < (landx - bound); ++x) for(UINT y = 0; y < (landy - bound); ++y)

	void addMount( LandscapeData landscape, float gx, float gy, float s, float h )
	{
		foreach_landxy(0)
		{
			float lx = abs(gx - (float)x); 
			float ly = abs(gy - (float)y);
			landscape[x][y] += h* landz * (1.0f / exp( s*sqrt(lx * lx + ly * ly)/landxh )); 
		}
	}

	void generateLandscape( LandscapeData landscape )
	{
		memset( landscape, 0, landx*landy*sizeof(float) );
		
		addMount( landscape, landx/3, landyh, 9, 1.2f );
		addMount( landscape, landxh, landyh,7 , 0.8f );
		addMount( landscape, landx*0.75f, landyh,11 , 1.0f );
		addMount( landscape, landxh, 0.2*landy,8 , 1.3f );
		addMount( landscape, landxh, 0.9*landy, 5 , 3.3f );
	}

	namespace
	{ 
		// helper functions for GenerageLandscapeView
		DWORD index(int x, int y) 
		{ 
			x = fit( x, 0, landx - 1);
			y = fit( y, 0, landy - 1);
			return y + landy*x; 
		}

		
		DWORD* writeface( UINT x, UINT y, DWORD* ind )
		{
			// write 2 triangles, based on xy landscape point
			*ind = index(x,y); 
			*(ind+2) = index(x, y+1);
			*(ind+1) = index(x+1, y+1); 
			*(ind+3) = *ind;
			*(ind+5) = *(ind+1);
			*(ind+4) = index(x+1, y);
			return ind + 6;
		}
	}

	hres generateLandscapeView( LandscapeData landscape, PDevice device, PMesh& landscapeView)
	{
		struct LandscapeVertex
		{
			Vector3 position; 
			Vector3 normal;   
		};

		// create mesh
		checked( D3DXCreateMeshFVF( landx*landy*2, landx*landy, D3DXMESH_32BIT|D3DXMESH_MANAGED, (D3DFVF_XYZ|D3DFVF_NORMAL), device, &landscapeView) )
	
		// fill vertex buffer
		LandscapeVertex *vertex;
		checked( landscapeView->LockVertexBuffer(D3DLOCK_DISCARD, (void**) &vertex) )
		foreach_landxy(0) 
		{
			vertex[index(x,y)].position = Vector3( (float)(x) - landx/2, (float)(y) -landy/2, landscape[x][y] );
		}
		landscapeView->UnlockVertexBuffer();
		
		// fill index buffer
		DWORD* index;
		checked( landscapeView->LockIndexBuffer(D3DLOCK_DISCARD, (void**) &index) )
		foreach_landxy(1) 
		{
			index = writeface( x, y, index );
		}
		landscapeView->UnlockIndexBuffer();
		
		// fill attribute buffer
		DWORD* attributeBuffer = 0;
		checked( landscapeView->LockAttributeBuffer(0, &attributeBuffer) )
		memset( attributeBuffer, 0, landx*landy*3 );
		landscapeView->UnlockAttributeBuffer();
		

		// optimize and build normals
		DWORD* adj = new DWORD[ landx*landy* 2 * 3 ];
		
		checked( landscapeView->GenerateAdjacency(0.0f, adj) )
		checked( landscapeView->OptimizeInplace( D3DXMESHOPT_ATTRSORT |
												 D3DXMESHOPT_COMPACT |
												 D3DXMESHOPT_VERTEXCACHE, adj, 0, 0, 0 ) )
		checked( D3DXComputeNormals( landscapeView, NULL ) )
		delete[] adj;

		return S_OK;
	}

	// -------------

	LandscapeRender::LandscapeRender( PDevice device ):RenderObject( zero, Color( 0, 0.3f, 1, 1 ) )
	{
		generateLandscape( data );
		generateLandscapeView( data, device, mesh );
		
		createTexture( device, landx, landy, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, heightMap );
		DWORD* heightData = new DWORD[landx*landy];
		foreach_landxy(0) 
		{
			//heightData[index(x,y)] = float_to_color( data[x][y] );
			float h = data[x][y];
			heightData[index(x,y)] = Color( h/landz, h/landz, h/landz, 1 );
		}
		writeTexture( heightMap, heightData, sizeof(DWORD)*landx*landy );
		//D3DXSaveTextureToFile( L"heightmap.png", D3DXIFF_PNG, heightMap, 0 );
		delete[] heightData;
	}

	PTexture LandscapeRender::GetHeightTexture(){
		return heightMap;
	}

	void LandscapeRender::GetMoveHere( const Vector3& pos, Vector3& suggest )
	{
		const Vector3 landbase = Vector3( -landx/2,-landy/2, 0 );
		Vector3 offset = pos - landbase;
		
		suggest.x = fit( pos.x, -landxh, landxh );
		suggest.y = fit( pos.y, -landyh, landyh );
		suggest.z = fit( pos.z, ((float*)data)[index((int)offset.x,(int)offset.y)], FLT_MAX);
	}
}